#!/usr/bin/env python

## Written by demure (demuredemeanor)
## This program depends on pactl to work
## This tool makes per application muting a simple and fast process.

from __future__ import print_function   ## Needed for python2/3 print
import sys                              ## Needed for argv
import subprocess                       ## Need for running `pactl`
import re                               ## Needed for regex


## Get overall sink state
def get_state():
    state = {}

    ## Get current sink states, decode needed for python3 strong types
    stream = subprocess.check_output(["pactl", "list", "sink-inputs"]).decode('utf-8')

    ## Take "Sink Input" and application.process.binary and assign to dict.
    ## Assign mute state for each sink
    for nums,bins,mutes in zip(re.findall("Sink Input #(\d+)", stream),\
                                re.findall("application.process.binary\s=\s\"(\S+)\"", stream),\
                                re.findall("Mute:\s(\w+)", stream)):
        state.update({nums:[bins, mutes]})
    return state


## Print Sink Numbers, binary name, and mute state -- if present
def print_state():
    state = get_state()

    if bool(state):
        for sink in state:
            print("Sink Number:", sink, "\tMuted:", state[sink][1], "\tbin:", state[sink][0])
    else:
        print("No applications are reported to be outputting souund.")
    return


## Checks if sink exists, then toggles if present
def mute_toggle(sink):
    state = get_state()
    if sink in state:
        subprocess.check_output(["pactl", "set-sink-input-mute", sink,  "toggle"])
    else:
        print("Sink number [" + sink + "] not found!")
    return


## Checks state for string, if single result will pass to mute_toggle
def string_to_sink(string):
    state = get_state()
    matches=[]
    for nums in state:
        if state[nums][0] == string:
            matches.append(nums)
    if len(matches) > 1:
        print("Too many matches!")
    elif len(matches) == 0:
        print("No matches.")
    else:
        mute_toggle(matches[0])
    return


## Parse the command line arguments
def parse_argv(argv):
    arg1=argv[1]

    ## Single augment
    if len(argv) == 2:
        ## Option list
        if arg1 == "-l" or arg1 == "--list" or arg1 == "list":
            print_state()
            return

        ## Option mute fail
        if arg1 == "-m" or arg1 == "--mute" or arg1 == "mute":
            print("Missing SINK number!")
            msg_help()
            return

        ## Option string fail
        if arg1 == "-s" or arg1 == "--string" or arg1 == "string":
            print("Missing string NAME!")
            return

        ## Option help
        if arg1 == "-h" or arg1 == "--help" or arg1 == "help":
            msg_help()
            return

        ## Option version
        if arg1 == "-V" or arg1 == "--version" or arg1 == "version":
            msg_version()
            return

        ## Run -m if only sink number passed
        if re.match("^(\d+)$", arg1):
            mute_toggle(arg1)
            return

        ## If they get here, the didn't match; print help.
        print("["+arg1+"] is not a valid option!")
        msg_help()
        return

    ## Two Augments
    elif len(argv) == 3:
        arg2=argv[2]

        ## Option mute
        if arg1 == "-m" or arg1 == "--mute" or arg1 == "mute":
            if re.match("^(\d+)$", arg2):
                mute_toggle(arg2)
            else:
                print("["+arg2+"] is not a valid sink input!")
                msg_help()
            return

        ## Option string
        if arg1 == "-s" or arg1 == "--string" or arg1 == "string":
            string_to_sink(arg2)
        else:
            print("["+arg1+"] is not a valid option!")
            msg_help()
        return

    ## Too many Augments
    else:
        print("Too many augments!")
        msg_help()
    return


## Print help message
def msg_help():
    help_msg=\
"""
Usage: premute [OPTION...]
    When run without OPTION, will output sink list
    When run with SINK as option, will mute SINK number

    -l, --list              List Sink Inputs
    -m, --mute SINK         Mute Sink number
    -s, --string NAME       Attempt bin name mute
    -V, --version           Display version information
    -h, --help              Display this help message
"""
    print(help_msg)
    return


## Print version number message
def msg_version():
    version_msg="permute version: 0.2.0"
    print(version_msg)
    return


## Main
def main(argv):
    if len(argv) > 1:
        parse_argv(argv)
    else:
        print_state()


if __name__ == '__main__':
    main(sys.argv)
